#!/usr/bin/perl

# ---------- Init variables -------
#my $version = "0.0001";
# ---------- external readable parameters -------
## Functions
##-------------------------------------------------------
#
# stict syntax checking.
use strict;
use CGI qw(param);
use SIM_settings;
use SIM_base;
use SIM_nodeInfo;
use SIM_cgiSettings;
use lib '../lib';
use GraphViz;



# Startupscript casetool, part of Sipman.

my $module = $ARGV[0];
my $action = $ARGV[1];
my $outputType = $ARGV[2];
my $option1 = $ARGV[3];
my $option2 = $ARGV[4];
my $option3 = $ARGV[5];


my $backHeader="black";
my %createdHosts = ();
my %testedHosts = ();
my %cdpTested = ();
my %connector = ();
my $maxNodes = 250;
my $testedNodes = 0;
#my $g = GraphViz->new(layout => 'neato', ratio => 'compress'); # this will start the map.
#my $g = GraphViz->new(layout => 'neato'); # this will start the map.
#my $g = GraphViz->new(layout => 'twopi', ranksep => '3', ratio => 'auto'); # this will start the map.
#my $g = GraphViz->new(layout => 'fdp', ranksep => '3', ratio => 'auto', overlap => 'scale'); # this will start the map.
#my $g = GraphViz->new(layout => 'twopi', ranksep => '3', ratio => 'auto', overlap => 'scale'); # this will start the map.
#directed => '0' om arrows te skippen.
#rankdir => grafiek meer links-rechst i.p.v. boven beneden.
#random_start => '1' mogelijk betere verdeling punten.
#overlap (compress, scale, scalexy, 
#my $g = GraphViz->new(layout => 'neato', ranksep => '3', ratio => 'auto', overlap => 'scale'); # this will start the map.
#my $g = GraphViz->new(layout => 'neato', ranksep => '3', ratio => 'auto', overlap => 'scale', directed => '0'); # this will start the map.
my $g = GraphViz->new(layout => 'neato', ranksep => '3', ratio => 'auto', overlap => 'scale', directed => '0', random_start => '1'); # this will start the map.


sub printMainMenu {
  my $fontColor = "#222222";
  print("<TABLE WIDTH=100% BORDER=0 CELLSPACING=0><TR BGCOLOR=#EEEEEE><TD>
    <CENTER><TABLE><TR><TD VALIGN=top><CENTER>
    <A HREF=$httpScriptName?$module+allSnmpDevices><FONT COLOR=$fontColor>All-snmp</FONT></A></TD>
    <TD VALIGN=top><FORM method=post Action=$httpScriptName?$module+dispDeviceType>
      Device type
      <SELECT NAME=deviceType SIZE=1>");
  chdir $deviceDir;
  my @deviceTypes = <*>;
  foreach my $entry (@deviceTypes) {
    print("<OPTION VALUE=$entry>$entry</OPTION>");
  }
  print("</SELECT><INPUT TYPE=submit Value=\"Build map\"></FORM>
      </TD></TR></TABLE>
    </TD></TR></TABLE>
    </TD></TR></TABLE>
        ");
}

sub getHostName {
  my $ipAddr = "@_[0]";
  my $workDir = getIpDir( $ipAddr );
  my $hostName;
  if ( -f "$workDir/hostname.txt" ) {
    $hostName = catFileLine( "$workDir/hostname.txt" );
  } else {
    $hostName = catFileLine( "$workDir/hostname-cdp" );
  }
  $hostName = lc($hostName);
  if ( "$hostName" ne "" ) {
    my ( $hostName, $dummy ) = split('\.', $hostName);
    return $hostName;
  }
}

sub searchMac {
  my $ipAddr = "@_[0]";
  my $workDir = getIpDir( $ipAddr );
  my $macAddr;
  if ( -f "$workDir/firstmac.txt" ) {
    $macAddr = catFileLine( "$workDir/firstmac.txt" );
  } elsif ( -f "$workDir/arpmac.txt" ) {
    $macAddr = catFileLine( "$workDir/arpmac.txt" );
  } else {
    printDebug("1", "Oops, no mac found for $workDir ($ipAddr).");
  }
  #print("Found macaddr: $macAddr.\n");
  return ("$macAddr");
}



sub findNeighb {
  my $ipList = "@_[0]";
  printDebug("1","Running findNeighb ($ipList)");
  my $newList = "";
  # use an iplist to keep the model as balanced as possible.
  my @ipEntries = split(' ', $ipList);
  foreach my $ipAddr (@ipEntries) {
    printDebug("1", "Working on node $ipAddr\n");
#   sleep 2;
    my $macAddr = searchMac( $ipAddr );
    my $hostName = getHostName( $ipAddr );
    printDebug("1", "searchMac: macAddr=$macAddr, hostName=$hostName, ipAddr=$ipAddr");
    if ( "$hostName" eq "" ) {
      printDebug("1", "No hostname found for $ipAddr.\n");
    } elsif ( "$cdpTested{$hostName}" ne "" ) {
      printDebug("1", "Entry for $hostName ($ipAddr) already polled.\n");
    } elsif ( "$macAddr" ne "" ) {
      # only discover nodes with an arpMac or firstMac (since we need int info).
      printDebug("1", "findNeighb: testing $hostName ($ipAddr).\n");
      $cdpTested{$hostName} = $ipAddr;
#     print("searching for $macDatabaseDir/$macAddr/cdpneighb-*");
      my @cdpList = <$macDatabaseDir/$macAddr/cdpneighb-*>;
      if ( "$cdpList[0]" ne "" ) {
        printDebug("1", "Testing cdp info for $ipAddr on $hostName, need to test interfaces.");
        foreach my $entry (@cdpList) {
#         print(" b $entry  <BR>");
          my $neighbInfo = catFileLine( "$entry" );
          my ( $neighIp, $dummy ) = split(' ', $neighbInfo);
#         print("Found neighbour $neighIp on $ipAddr\n");
          my $found = testNeighb( $neighIp );
          $newList = "$newList $found";
        }
      } else {
        printDebug("1", "No cdp info for $ipAddr on $hostName, need to test interfaces.");
        my @intList = <$macDatabaseDir/$macAddr/ipint-*>;
        my $foundIntLink = "no";
        foreach my $entry (@intList) {
          my $intEntry = catFileLine( "$entry" );
          my ( $intIpAddr, $rest ) = split(' ', $intEntry);
          my $ipDir = getIpDir( $intIpAddr );
          my $macAddr = catFileLine("$ipDir/snmpmac.txt");
          if (( "$macAddr" ne "" ) and ( -f "$macDatabaseDir/$macAddr/switchport")) {
	    $foundIntLink = "yes";
            my $switchInfo = catFileLine("$macDatabaseDir/$macAddr/switchport");
            my ( $switchname, $rest ) = split(' ', $switchInfo);
            printDebug("1", "findNeighb: addEdge link for $intIpAddr, $macAddr, $hostName to $switchname.");
            addEdge( $hostName, $switchname );
          }
        }
	# some snmp devices don't report an snmp interface, use the arp-mac instead.
	if ( "$foundIntLink" ne "yes" ) {
	  printDebug("1", "findNeighb: testArpMac $ipAddr ($hostName)");
	  my $ipDir = getIpDir( $ipAddr );
	  my $macAddr = catFileLine("$ipDir/arpmac.txt");
	  if (( "$macAddr" ne "" ) and ( -f "$macDatabaseDir/$macAddr/switchport")) {
	    my $switchInfo = catFileLine("$macDatabaseDir/$macAddr/switchport");
	    my ( $switchname, $rest ) = split(' ', $switchInfo);
	    addEdge( $hostName, $switchname );
	  }
	}
      }
#   } else {
#     printDebug("0", "no mac for $ipAddr on $hostName, skipping.");
    }
  }
  printDebug("1", "Found new neighbourlist: \"$newList\".\n");
  if ( "$newList" ne "" ) {
    findNeighb( $newList );
  } else {
    return "none";
  }
}

sub addEdge {
  my $sourceHost = "@_[0]";
  my $destHost = "@_[1]";
  my $revName = "$destHost-$sourceHost";
  my $forwName = "$sourceHost-$destHost";
  if (( "$sourceHost" ne "" ) and ( "$destHost" ne "" ) and ( "$connector{$revName}" eq "" )) {
    $connector{$forwName} = "yes";
    printDebug("1", "testNeighb: g->add_edge($sourceHost=&gt; $destHost)");
    $g->add_edge($sourceHost=> $destHost);
  }
}


sub testNeighb {
  my $ipAddr = "@_[0]";
  my $hostName = getHostName( $ipAddr );
  my $neighbList;
# print("Found hostname \"$hostName\". ");
  if (( "$hostName" ne "" ) and ( $testedNodes < $maxNodes )){
#   print("Testing. ");
    # test if we already tested this one.
    if ( "$testedHosts{$hostName}" eq "" ) {
      $testedHosts{$hostName} = "$ipAddr";
      my $macAddr = searchMac( $ipAddr );
      if ( "$macAddr" ne "" ) {
#       print( "Found mac $macAddr.\n");
        my @cdpList = <$macDatabaseDir/$macAddr/cdpneighb-*>;
        foreach my $entry (@cdpList) {
          my $neighbInfo = catFileLine( "$entry" );
          my ( $neighIp, $dummy ) = split(' ', $neighbInfo);
          my $neighName = getHostName( $neighIp );
          # if already defind, considder parrent.
          makeEntry( $neighIp, $neighName );
          addEdge( $hostName, $neighName );
        }
      }
    }
#   print("returning $ipAddr\n");
    return $ipAddr;
# } else {
#   print("Hostname for $ipAddr empty.\n");
  }
# print("<BR>");
  return "";
}


sub makeEntry {
  my $ipAddr = "@_[0]";
  my $nodeName = "@_[1]";
  # use this entry to set all node info like shape, color etc...
  # fontname  =>'arial'
  if (( "$createdHosts{$nodeName}" eq "" ) and ( "$nodeName" ne "" )) {
    $testedNodes++;
    #my $nodecolor = "greenyellow";
    my $nodecolor = testNodeStatus( $ipAddr );
    printDebug("1", "makeEntry: g->add_node($nodeName, color =&gt; '$nodecolor'");
    $createdHosts{$nodeName} = $ipAddr;
#   $g->add_node($nodeName, color => $nodecolor, style => 'filled', fontname  => 'arial', URL => "/sipman3-cgi/nph-sipman3.cgi?mon+searchSeed+searchIndex+$nodeName", TARGET => "_parent");
    $g->add_node($nodeName, color => $nodecolor, style => 'filled', fontname  => 'arial', URL => "/sipman3-cgi/nph-sipman3.cgi?mon+searchSeed+searchIndex+$nodeName", TARGET => "_blank");
    #$g->add_node($nodeName, color => $nodecolor, style => 'filled', fontname  => 'arial', URL => "/sipman3-cgi/nph-sipman3.cgi?mon+searchSeed+searchIndex+$nodeName");
    #$g->add_node($nodeName, color => 'greenyellow', style => 'filled');
  }
}

sub searchSwitch {
  my $ipAddr = "@_[0]";
  my $hostname = "@_[1]";
  my $macAddr = searchMac( $ipAddr );
  printDebug("1",  "searchSwitch: ipAddr=$ipAddr, hostname=$hostname.<HR>");
  if (( "$macAddr" ne "" ) and ( -f "$macDatabaseDir/$macAddr/switchport")) {
    my $switchInfo = catFileLine( "$macDatabaseDir/$macAddr/switchport");
    my ( $switchName, $rest ) = split(' ', $switchInfo);
    my $switchInfo = catFileLine( "$switchInfoDir/devices/$switchName/switchip");
    my ( $switchIp, $dummy ) = split(' ', $switchInfo);
    printDebug("1",  "switchName=$switchName, switchIp=$switchIp\n");
    if (( "$switchIp" ne "" ) and ( "$switchName" ne "" )) {
      makeEntry( $switchIp, $switchName );
      addEdge( $hostname, $switchName);
      findNeighb( $switchIp );
    }
    return ("$switchName");
  }
}


sub testNode {
  my $ipAddr = "@_[0]";
  my $hostname = getHostName( $ipAddr );
  if (( "$hostname" ne "" ) and ( "$testedHosts{$hostname}" eq "" )) {
    makeEntry( $ipAddr, $hostname );
    my $found = findNeighb( $ipAddr );
    if ( "$found" eq "none" ) {
      searchSwitch( $ipAddr, $hostname );
    }
  }
  return $hostname;
}

sub listDeviceType {
  my $deviceType = "@_[0]";
  if ( -d "$deviceDir/$deviceType" ) {
    my @deviceList = <$deviceDir/$deviceType/*>;
    foreach my $entry (@deviceList) {
      printDebug("1", "deviceType: entry=$entry");
      my $deviceIp = catFileLine("$entry/ipaddr");
      testNode( $deviceIp );
    }
  }
}


# ------------------------------------- Main ---------------------------------------

&printMainMenu;
if ( "$action" eq "none" ) {
  $action = "allSnmpDevices";
}

if ( "$outputType" eq "none" ) {
  $outputType = "svg";
}

my $displayFile = "";
if ( "$action" eq "displayNode" ) {
  my $hostname = testNode( $option1 );
  if ( "$hostname" ne "" ) {
    testNode( $option1 );
    $displayFile = "map-$hostname";
  } else {
    print("Sorry, no hostname found for $option1, no map generated.");
  }
} elsif ( "$action" eq "dispDeviceType" ) {
  my $deviceType = param("deviceType");
  if ( -d "$deviceDir/$deviceType" ) {
#   print("$deviceDir/$deviceType");
    listDeviceType( $deviceType );
    $displayFile = "map-$option1";
  }
} elsif ( "$action" eq "allSnmpDevices" ) {
  chdir $deviceDir;
  my @deviceTypes = <*>;
  foreach my $entry (@deviceTypes) {
    printDebug("1", "allDevices: entry=$entry");
    listDeviceType( $entry );
  }
  $displayFile = "map-allDevices";
} else {
  print("Sorry no valid option found ($action).");
}
print("BETA ");
if ( "$displayFile" ne "" ) {
  print("<CENTER>Generating map, witdh:
    <A HREF=$httpScriptName?map+$action+svg+$option1+50%>50%</A> -
    <A HREF=$httpScriptName?map+$action+svg+$option1+70%>70%</A> -
    <A HREF=$httpScriptName?map+$action+svg+$option1+100%>100%</A> -
    <A HREF=$httpScriptName?map+$action+svg+$option1+2000>2000</A> -
    <A HREF=$httpScriptName?map+$action+svg+$option1+4000>4000</A> -
    <A HREF=$httpScriptName?map+$action+svg+$option1+8000>8000</A> -
    <A HREF=$httpScriptName?map+$action+text+$option1>text</A> -
  <HR>");
  if ( "$outputType" eq "svg" ) {
    $g->as_svg("$graphDir/$displayFile.svg");
    #print("<A HREF=/graphs/$displayFile.png><IMG SRC=/graphs/$displayFile.png ALT=\"dynamic map\" WIDTH=98%></A>");
#   print(" option2=$option2 ");
    if ( "$option2" eq "" ) {
      print("<object data=\"/graphs/$displayFile.svg\" type=\"image/svg+xml\" width=100% height=100%></object>");
      #print("<embed src=\"/graphs/$displayFile.svg\" type=\"image/svg+xml\" width=100% height=100% pluginspage=\"http://www.adobe.com/svg/viewer/install/\" /></embed>");
      #print("<IFRAME SRC=/graphs/$displayFile.svg width=100% height=100%></IFRAME>");
    } else {
      print("<object data=\"/graphs/$displayFile.svg\" type=\"image/svg+xml\" width=$option2 height=$option2></object>");
    }
  } elsif ( "$outputType" eq "text" ) {
    $g->as_text("$graphDir/$displayFile.txt");
    print("</CENTER><PRE>");
    open( TXT, "$graphDir/$displayFile.txt");
    my @text = <TXT>;
    foreach my $line (@text) {
      print("$line");
    }
    print("</PRE>");
  }
}
#&printFooter;
